/* -------------------------------------------------------------------------
 *
 * contrib/sepgsql/selinux.c
 *
 * 用户空间与内核空间中的 selinux 交互，使用 libselinux api。
 *
 * Copyright (c) 2010-2022, PostgreSQL Global Development Group
 *
 * -------------------------------------------------------------------------
 */
#include "postgres.h"

#include "lib/stringinfo.h"

#include "sepgsql.h"

/*
 * selinux_catalog
 *
 * 此映射表用于将对象类和访问向量的名称转换为其自己的代码。
 * 当我们询问SELinux所需的权限是否被允许时，
 * 我们使用security_compute_av(3)。它需要我们使用在安全策略中定义的“外部”代码表示对象类
 * 和访问向量。这是在运行时确定的，而不是构建时。因此，它需要一个内部
 * 服务将我们想要检查的对象类/访问向量转换为内核希望给予的代码。
 */
static struct
{
	const char *class_name;
	uint16		class_code;
	struct
	{
		const char *av_name;
		uint32		av_code;
	}			av[32];
}			selinux_catalog[] =

{
	{
		"process", SEPG_CLASS_PROCESS,
		{
			{
				"transition", SEPG_PROCESS__TRANSITION
			},
			{
				"dyntransition", SEPG_PROCESS__DYNTRANSITION
			},
			{
				"setcurrent", SEPG_PROCESS__SETCURRENT
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"file", SEPG_CLASS_FILE,
		{
			{
				"read", SEPG_FILE__READ
			},
			{
				"write", SEPG_FILE__WRITE
			},
			{
				"create", SEPG_FILE__CREATE
			},
			{
				"getattr", SEPG_FILE__GETATTR
			},
			{
				"unlink", SEPG_FILE__UNLINK
			},
			{
				"rename", SEPG_FILE__RENAME
			},
			{
				"append", SEPG_FILE__APPEND
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"dir", SEPG_CLASS_DIR,
		{
			{
				"read", SEPG_DIR__READ
			},
			{
				"write", SEPG_DIR__WRITE
			},
			{
				"create", SEPG_DIR__CREATE
			},
			{
				"getattr", SEPG_DIR__GETATTR
			},
			{
				"unlink", SEPG_DIR__UNLINK
			},
			{
				"rename", SEPG_DIR__RENAME
			},
			{
				"search", SEPG_DIR__SEARCH
			},
			{
				"add_name", SEPG_DIR__ADD_NAME
			},
			{
				"remove_name", SEPG_DIR__REMOVE_NAME
			},
			{
				"rmdir", SEPG_DIR__RMDIR
			},
			{
				"reparent", SEPG_DIR__REPARENT
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"lnk_file", SEPG_CLASS_LNK_FILE,
		{
			{
				"read", SEPG_LNK_FILE__READ
			},
			{
				"write", SEPG_LNK_FILE__WRITE
			},
			{
				"create", SEPG_LNK_FILE__CREATE
			},
			{
				"getattr", SEPG_LNK_FILE__GETATTR
			},
			{
				"unlink", SEPG_LNK_FILE__UNLINK
			},
			{
				"rename", SEPG_LNK_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"chr_file", SEPG_CLASS_CHR_FILE,
		{
			{
				"read", SEPG_CHR_FILE__READ
			},
			{
				"write", SEPG_CHR_FILE__WRITE
			},
			{
				"create", SEPG_CHR_FILE__CREATE
			},
			{
				"getattr", SEPG_CHR_FILE__GETATTR
			},
			{
				"unlink", SEPG_CHR_FILE__UNLINK
			},
			{
				"rename", SEPG_CHR_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"blk_file", SEPG_CLASS_BLK_FILE,
		{
			{
				"read", SEPG_BLK_FILE__READ
			},
			{
				"write", SEPG_BLK_FILE__WRITE
			},
			{
				"create", SEPG_BLK_FILE__CREATE
			},
			{
				"getattr", SEPG_BLK_FILE__GETATTR
			},
			{
				"unlink", SEPG_BLK_FILE__UNLINK
			},
			{
				"rename", SEPG_BLK_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"sock_file", SEPG_CLASS_SOCK_FILE,
		{
			{
				"read", SEPG_SOCK_FILE__READ
			},
			{
				"write", SEPG_SOCK_FILE__WRITE
			},
			{
				"create", SEPG_SOCK_FILE__CREATE
			},
			{
				"getattr", SEPG_SOCK_FILE__GETATTR
			},
			{
				"unlink", SEPG_SOCK_FILE__UNLINK
			},
			{
				"rename", SEPG_SOCK_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"fifo_file", SEPG_CLASS_FIFO_FILE,
		{
			{
				"read", SEPG_FIFO_FILE__READ
			},
			{
				"write", SEPG_FIFO_FILE__WRITE
			},
			{
				"create", SEPG_FIFO_FILE__CREATE
			},
			{
				"getattr", SEPG_FIFO_FILE__GETATTR
			},
			{
				"unlink", SEPG_FIFO_FILE__UNLINK
			},
			{
				"rename", SEPG_FIFO_FILE__RENAME
			},
			{
				NULL, 0UL
			}
		}
	},
	{
		"db_database", SEPG_CLASS_DB_DATABASE,
		{
			{
				"create", SEPG_DB_DATABASE__CREATE
			},
			{
				"drop", SEPG_DB_DATABASE__DROP
			},
			{
				"getattr", SEPG_DB_DATABASE__GETATTR
			},
			{
				"setattr", SEPG_DB_DATABASE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_DATABASE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_DATABASE__RELABELTO
			},
			{
				"access", SEPG_DB_DATABASE__ACCESS
			},
			{
				"load_module", SEPG_DB_DATABASE__LOAD_MODULE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_schema", SEPG_CLASS_DB_SCHEMA,
		{
			{
				"create", SEPG_DB_SCHEMA__CREATE
			},
			{
				"drop", SEPG_DB_SCHEMA__DROP
			},
			{
				"getattr", SEPG_DB_SCHEMA__GETATTR
			},
			{
				"setattr", SEPG_DB_SCHEMA__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_SCHEMA__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_SCHEMA__RELABELTO
			},
			{
				"search", SEPG_DB_SCHEMA__SEARCH
			},
			{
				"add_name", SEPG_DB_SCHEMA__ADD_NAME
			},
			{
				"remove_name", SEPG_DB_SCHEMA__REMOVE_NAME
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_table", SEPG_CLASS_DB_TABLE,
		{
			{
				"create", SEPG_DB_TABLE__CREATE
			},
			{
				"drop", SEPG_DB_TABLE__DROP
			},
			{
				"getattr", SEPG_DB_TABLE__GETATTR
			},
			{
				"setattr", SEPG_DB_TABLE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_TABLE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_TABLE__RELABELTO
			},
			{
				"select", SEPG_DB_TABLE__SELECT
			},
			{
				"update", SEPG_DB_TABLE__UPDATE
			},
			{
				"insert", SEPG_DB_TABLE__INSERT
			},
			{
				"delete", SEPG_DB_TABLE__DELETE
			},
			{
				"lock", SEPG_DB_TABLE__LOCK
			},
			{
				"truncate", SEPG_DB_TABLE__TRUNCATE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_sequence", SEPG_CLASS_DB_SEQUENCE,
		{
			{
				"create", SEPG_DB_SEQUENCE__CREATE
			},
			{
				"drop", SEPG_DB_SEQUENCE__DROP
			},
			{
				"getattr", SEPG_DB_SEQUENCE__GETATTR
			},
			{
				"setattr", SEPG_DB_SEQUENCE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_SEQUENCE__RELABELTO
			},
			{
				"get_value", SEPG_DB_SEQUENCE__GET_VALUE
			},
			{
				"next_value", SEPG_DB_SEQUENCE__NEXT_VALUE
			},
			{
				"set_value", SEPG_DB_SEQUENCE__SET_VALUE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_procedure", SEPG_CLASS_DB_PROCEDURE,
		{
			{
				"create", SEPG_DB_PROCEDURE__CREATE
			},
			{
				"drop", SEPG_DB_PROCEDURE__DROP
			},
			{
				"getattr", SEPG_DB_PROCEDURE__GETATTR
			},
			{
				"setattr", SEPG_DB_PROCEDURE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_PROCEDURE__RELABELTO
			},
			{
				"execute", SEPG_DB_PROCEDURE__EXECUTE
			},
			{
				"entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT
			},
			{
				"install", SEPG_DB_PROCEDURE__INSTALL
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_column", SEPG_CLASS_DB_COLUMN,
		{
			{
				"create", SEPG_DB_COLUMN__CREATE
			},
			{
				"drop", SEPG_DB_COLUMN__DROP
			},
			{
				"getattr", SEPG_DB_COLUMN__GETATTR
			},
			{
				"setattr", SEPG_DB_COLUMN__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_COLUMN__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_COLUMN__RELABELTO
			},
			{
				"select", SEPG_DB_COLUMN__SELECT
			},
			{
				"update", SEPG_DB_COLUMN__UPDATE
			},
			{
				"insert", SEPG_DB_COLUMN__INSERT
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_tuple", SEPG_CLASS_DB_TUPLE,
		{
			{
				"relabelfrom", SEPG_DB_TUPLE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_TUPLE__RELABELTO
			},
			{
				"select", SEPG_DB_TUPLE__SELECT
			},
			{
				"update", SEPG_DB_TUPLE__UPDATE
			},
			{
				"insert", SEPG_DB_TUPLE__INSERT
			},
			{
				"delete", SEPG_DB_TUPLE__DELETE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_blob", SEPG_CLASS_DB_BLOB,
		{
			{
				"create", SEPG_DB_BLOB__CREATE
			},
			{
				"drop", SEPG_DB_BLOB__DROP
			},
			{
				"getattr", SEPG_DB_BLOB__GETATTR
			},
			{
				"setattr", SEPG_DB_BLOB__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_BLOB__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_BLOB__RELABELTO
			},
			{
				"read", SEPG_DB_BLOB__READ
			},
			{
				"write", SEPG_DB_BLOB__WRITE
			},
			{
				"import", SEPG_DB_BLOB__IMPORT
			},
			{
				"export", SEPG_DB_BLOB__EXPORT
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_language", SEPG_CLASS_DB_LANGUAGE,
		{
			{
				"create", SEPG_DB_LANGUAGE__CREATE
			},
			{
				"drop", SEPG_DB_LANGUAGE__DROP
			},
			{
				"getattr", SEPG_DB_LANGUAGE__GETATTR
			},
			{
				"setattr", SEPG_DB_LANGUAGE__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_LANGUAGE__RELABELTO
			},
			{
				"implement", SEPG_DB_LANGUAGE__IMPLEMENT
			},
			{
				"execute", SEPG_DB_LANGUAGE__EXECUTE
			},
			{
				NULL, 0UL
			},
		}
	},
	{
		"db_view", SEPG_CLASS_DB_VIEW,
		{
			{
				"create", SEPG_DB_VIEW__CREATE
			},
			{
				"drop", SEPG_DB_VIEW__DROP
			},
			{
				"getattr", SEPG_DB_VIEW__GETATTR
			},
			{
				"setattr", SEPG_DB_VIEW__SETATTR
			},
			{
				"relabelfrom", SEPG_DB_VIEW__RELABELFROM
			},
			{
				"relabelto", SEPG_DB_VIEW__RELABELTO
			},
			{
				"expand", SEPG_DB_VIEW__EXPAND
			},
			{
				NULL, 0UL
			},
		}
	},
};

/*
 * sepgsql_mode
 *
 * SEPGSQL_MODE_DISABLED: 在运行时禁用
 * SEPGSQL_MODE_DEFAULT: 同系统设置
 * SEPGSQL_MODE_PERMISSIVE: 始终处于宽容模式
 * SEPGSQL_MODE_INTERNAL: 与宽容模式相同，但没有审计日志
 */
static int	sepgsql_mode = SEPGSQL_MODE_INTERNAL;

/*
 * sepgsql_is_enabled
 */
bool sepgsql_is_enabled(void)
{
	return (sepgsql_mode != SEPGSQL_MODE_DISABLED);
}

/*
 * sepgsql_get_mode
 */
int sepgsql_get_mode(void)
{
	return sepgsql_mode;
}

/*
 * sepgsql_set_mode
 */
int sepgsql_set_mode(int fc_new_mode)
{
	int			fc_old_mode = sepgsql_mode;

	sepgsql_mode = fc_new_mode;

	return fc_old_mode;
}

/*
 * sepgsql_getenforce
 *
 * 它返回当前工作模式是否尝试强制执行访问控制决策。 当sepgsql_mode为
 * SEPGSQL_MODE_DEFAULT且系统处于强制模式时，将强制执行。
 */
bool sepgsql_getenforce(void)
{
	if (sepgsql_mode == SEPGSQL_MODE_DEFAULT &&
		selinux_status_getenforce() > 0)
		return true;

	return false;
}

/*
 * sepgsql_audit_log
 *
 * 它生成一个安全审计记录。它将审计记录写入标准PG的日志文件中。
 *
 * SELinux可以使用安全策略中的“auditdeny”和“auditallow”规则控制应该审计和不应该审计的内容。 默认情况下，所有的访问违规行为都会被审计，而所有的访问允许都不会被审计。 但是，我们可以建立安全策略，以便我们可以有例外。因此，有必要遵循来自安全策略的建议。 (av_decision.auditallow 和 auditdeny)
 *
 * 安全审计是一个重要特性，因为它使我们能够在发生安全事件时检查发生了什么。 实际上，ISO/IEC15408定义了几项审计功能的安全功能。
 */
void sepgsql_audit_log(bool fc_denied,
				  bool fc_enforcing,
				  const char *fc_scontext,
				  const char *fc_tcontext,
				  uint16 fc_tclass,
				  uint32 fc_audited,
				  const char *fc_audit_name)
{
	StringInfoData fc_buf;
	const char *fc_class_name;
	const char *fc_av_name;
	int			fc_i;

	/* 查找对象类的名称 */
	Assert(fc_tclass < SEPG_CLASS_MAX);
	fc_class_name = selinux_catalog[fc_tclass].class_name;

	/* 查找权限的名称 */
	initStringInfo(&fc_buf);
	appendStringInfo(&fc_buf, "%s {",
					 (fc_denied ? "denied" : "allowed"));
	for (fc_i = 0; selinux_catalog[fc_tclass].av[fc_i].av_name; fc_i++)
	{
		if (fc_audited & (1UL << fc_i))
		{
			fc_av_name = selinux_catalog[fc_tclass].av[fc_i].av_name;
			appendStringInfo(&fc_buf, " %s", fc_av_name);
		}
	}
	appendStringInfoString(&fc_buf, " }");

	/*
	 * 如果已加载，则调用外部审计模块
	 */
	appendStringInfo(&fc_buf, " scontext=%s tcontext=%s tclass=%s",
					 fc_scontext, fc_tcontext, fc_class_name);
	if (fc_audit_name)
		appendStringInfo(&fc_buf, " name=\"%s\"", fc_audit_name);

	if (fc_enforcing)
		appendStringInfoString(&fc_buf, " permissive=0");
	else
		appendStringInfoString(&fc_buf, " permissive=1");

	ereport(LOG, (errmsg("SELinux: %s", fc_buf.data)));
}

/*
 * sepgsql_compute_avd
 *
 * 它实际上询问SELinux在哪对安全上下文和对象类上允许哪些权限。 它还返回在访问违规或允许时应该审计的权限。
 * 在大多数情况下，主体的安全上下文（scontext）是客户端，而
 * 目标安全上下文（tcontext）是数据库对象。
 *
 * 访问控制决策应在给定的av_decision上设置。
 * av_decision.allowed具有SEPG_<class>__<perms>的位掩码
 * 以建议此对象类中允许的操作集。
 */
void sepgsql_compute_avd(const char *fc_scontext,
					const char *fc_tcontext,
					uint16 fc_tclass,
					struct av_decision *fc_avd)
{
	const char *fc_tclass_name;
	security_class_t fc_tclass_ex;
	struct av_decision fc_avd_ex;
	int			fc_i,
				fc_deny_unknown = security_deny_unknown();

	/* 获取对象类的外部代码 */
	Assert(fc_tclass < SEPG_CLASS_MAX);
	Assert(fc_tclass == selinux_catalog[fc_tclass].class_code);

	fc_tclass_name = selinux_catalog[fc_tclass].class_name;
	fc_tclass_ex = string_to_security_class(fc_tclass_name);

	if (fc_tclass_ex == 0)
	{
		/*
		 * 如果当前安全策略不支持对应于数据库对象的权限，
		 * 我们用虚拟数据填充它们。
		 * 如果security_deny_unknown()返回正值，未定义
		 * 的权限应被拒绝。否则，允许
		 */
		fc_avd->allowed = (security_deny_unknown() > 0 ? 0 : ~0);
		fc_avd->auditallow = 0U;
		fc_avd->auditdeny = ~0U;
		fc_avd->flags = 0;

		return;
	}

	/*
	 * 询问SELinux在一对安全上下文和给定对象类别上允许的权限集合。
	 */
	if (security_compute_av_flags_raw(fc_scontext,
									  fc_tcontext,
									  fc_tclass_ex, 0, &fc_avd_ex) < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("SELinux could not compute av_decision: "
						"scontext=%s tcontext=%s tclass=%s: %m",
						fc_scontext, fc_tcontext, fc_tclass_name)));

	/*
	 * SELinux将其访问控制决策作为一组权限返回，这些权限以依赖于运行时环境的外部代码表示。因此，
	 * 我们需要在返回结果给调用者之前将其翻译为内部表示。
	 */
	memset(fc_avd, 0, sizeof(struct av_decision));

	for (fc_i = 0; selinux_catalog[fc_tclass].av[fc_i].av_name; fc_i++)
	{
		access_vector_t fc_av_code_ex;
		const char *fc_av_name = selinux_catalog[fc_tclass].av[fc_i].av_name;
		uint32		fc_av_code = selinux_catalog[fc_tclass].av[fc_i].av_code;

		fc_av_code_ex = string_to_av_perm(fc_tclass_ex, fc_av_name);
		if (fc_av_code_ex == 0)
		{
			/* 填充未定义的权限 */
			if (!fc_deny_unknown)
				fc_avd->allowed |= fc_av_code;
			fc_avd->auditdeny |= fc_av_code;

			continue;
		}

		if (fc_avd_ex.allowed & fc_av_code_ex)
			fc_avd->allowed |= fc_av_code;
		if (fc_avd_ex.auditallow & fc_av_code_ex)
			fc_avd->auditallow |= fc_av_code;
		if (fc_avd_ex.auditdeny & fc_av_code_ex)
			fc_avd->auditdeny |= fc_av_code;
	}
}

/*
 * sepgsql_compute_create
 *
 * 它返回一个默认的安全上下文，以分配给新的数据库
 * 对象。SELinux根据客户端、拥有新对象的上层对象
 * 和对象类别的组合进行计算。
 *
 * 例如，当一个客户端（staff_u:staff_r:staff_t:s0）尝试在一个架构（system_u:object_r:sepgsql_schema_t:s0）中创建
 * 一个新表时，SELinux查找其安全策略。如果它对
 * 这些安全上下文和对象类别（db_table）的组合有特殊规则，
 * 它返回特殊规则建议的安全上下文。
 * 否则，它返回架构的安全上下文，保持不变。
 *
 * 我们期望调用者已经对
 * 给定的安全上下文进行了有效性/验证检查。
 *
 * scontext：主体的安全上下文（主要是对等进程）。
 * tcontext：上层数据库对象的安全上下文。
 * tclass：正在创建的新对象的类代码（SEPG_CLASS_*）
 */
char * sepgsql_compute_create(const char *fc_scontext,
					   const char *fc_tcontext,
					   uint16 fc_tclass,
					   const char *fc_objname)
{
	char	   *fc_ncontext;
	security_class_t fc_tclass_ex;
	const char *fc_tclass_name;
	char	   *fc_result;

	/* 获取对象类的外部代码 */
	Assert(fc_tclass < SEPG_CLASS_MAX);

	fc_tclass_name = selinux_catalog[fc_tclass].class_name;
	fc_tclass_ex = string_to_security_class(fc_tclass_name);

	/*
	 * 询问SELinux在一对安全上下文上给定对象类别的默认上下文是什么。
	 */
	if (security_compute_create_name_raw(fc_scontext,
										 fc_tcontext,
										 fc_tclass_ex,
										 fc_objname,
										 &fc_ncontext) < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("SELinux could not compute a new context: "
						"scontext=%s tcontext=%s tclass=%s: %m",
						fc_scontext, fc_tcontext, fc_tclass_name)));

	/*
	 * libselinux返回malloc()分配的字符串，因此我们需要将其复制到
	 * palloc()分配的区域。
	 */
	PG_TRY();
	{
		fc_result = pstrdup(fc_ncontext);
	}
	PG_FINALLY();
	{
		freecon(fc_ncontext);
	}
	PG_END_TRY();

	return fc_result;
}

/*
 * sepgsql_check_perms
 *
 * 它在没有用户空间缓存机制下做出访问控制决策。
 * 如果SELinux拒绝了对一对安全标签的所需访问，
 * 它将引发错误或返回false。
 *
 * scontext：主题的安全标签（主要是对等进程）
 * tcontext：被引用对象的安全标签
 * tclass：被引用对象的类代码（SEPG_CLASS_*）
 * required：所需权限的掩码（SEPG_<class>__<perm>）
 * audit_name：可读的对象名称用于审计日志，或NULL。
 * abort_on_violation：如果在访问违规时应引发错误，则为true
 */
bool sepgsql_check_perms(const char *fc_scontext,
					const char *fc_tcontext,
					uint16 fc_tclass,
					uint32 fc_required,
					const char *fc_audit_name,
					bool fc_abort_on_violation)
{
	struct av_decision fc_avd;
	uint32		fc_denied;
	uint32		fc_audited;
	bool		fc_result = true;
	bool		fc_enforcing;

	sepgsql_compute_avd(fc_scontext, fc_tcontext, fc_tclass, &fc_avd);

	fc_denied = fc_required & ~fc_avd.allowed;

	if (sepgsql_get_debug_audit())
		fc_audited = (fc_denied ? fc_denied : fc_required);
	else
		fc_audited = (fc_denied ? (fc_denied & fc_avd.auditdeny)
				   : (fc_required & fc_avd.auditallow));

	fc_enforcing = sepgsql_getenforce() > 0 &&
		(fc_avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) == 0;

	if (fc_denied && fc_enforcing)
		fc_result = false;

	/*
	 * 如果需要，它会记录请求的安全审计。但当
	 * SE-PgSQL执行"内部"模式时，它需要保持安静。
	 */
	if (fc_audited && sepgsql_mode != SEPGSQL_MODE_INTERNAL)
	{
		sepgsql_audit_log(fc_denied,
						  fc_enforcing,
						  fc_scontext,
						  fc_tcontext,
						  fc_tclass,
						  fc_audited,
						  fc_audit_name);
	}

	if (!fc_result && fc_abort_on_violation)
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("SELinux: security policy violation")));
	return fc_result;
}
